﻿#include <stdio.h>

#include "depth_peeling_demo.h"
#include "graphics/error_gl.h"
#include "graphics/render_device_fbo.h"
#include "graphics/image_texture_gl.h"
#include "graphics/program_glsl.h"
#include "graphics/shader_object.h"
#include "graphics/error_gl.h"
#include "graphics/utils.h"
#include "shapes/triangle_mesh.h"
#include "tools/obj_loader.h"
#include "common/timer.h"

namespace venus {
#define MAX_DEPTH 1.0
enum PeelingMethod { kNone, kDual, kFront2Back, kBucket };
const char *method_name[] = { "GL", "Dual Peeling", "Front to Back Peeling", 
    "Bucket Depth Peeling" };

DepthPeelingDemo::DepthPeelingDemo() 
    : GlutWindow("Depth Peeling Demo", 10, 0, 800, 600, GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH),
      camera_(Point(-0.268, 0, 0.255)), demo_ready_(false), 
      fps_counter_(FpsCounter::kOverFrames, 60) {
  camera_.set_min_dist(0.1);
  camera_.set_fovy(30.0);
  camera_.set_velocity(0, 0, 0.05);
  model_ = NULL;

  device_ = NULL;
  dual_peeling_device_ = NULL;
  bucket_peeling_device_ = NULL;

  Utils::SetNULL(front_depth_targets_, 2);
  Utils::SetNULL(front_color_targets_, 2);
  Utils::SetNULL(front_color_blender_target_);

  Utils::SetNULL(dual_back_temp_targets_, 2);
  Utils::SetNULL(dual_depth_targets_, 2);
  Utils::SetNULL(dual_front_blender_targets_, 2);
  Utils::SetNULL(dual_back_blender_target_);

  Utils::SetNULL(bucket_min_max_depth_target_);
  Utils::SetNULL(bucket_depth_targets_, 8);

  front_init_program_ = NULL;
  front_peeling_program_ = NULL;
  front_blend_program_ = NULL;
  front_final_program_ = NULL;

  for (int i = 0; i < 8; ++i) {
    mrt_[i] = GL_COLOR_ATTACHMENT0_EXT+i;
  }
  quad_display_list_ = 0;
}

DepthPeelingDemo::~DepthPeelingDemo() {
  delete model_;
  delete device_;
  delete dual_peeling_device_;
  delete bucket_peeling_device_;
  this->ReleaseShaders();
  this->DeleteFrontPeelingRenderTargets();
  this->DeleteDualPeelingRenderTargets();
  this->DeleteBucketPeelingRenderTargets();
}

bool DepthPeelingDemo::Initialize(const char *model_file) {
  GLenum error = glewInit();
  if (GLEW_OK != error) {
    Error("%s", glewGetErrorString(error));
    return false;
  }
  //load model
  model_ = LoadObj(model_file);
  if (NULL == model_) {
    Error("DepthPeelingDemo::Initialize, load model failed");
    return false;
  }
  Info("triangles: %d", model_->triangle_count());
  model_->Scale2Size(0.2);
  model_->Translate2Pos(Point(0));
  model_gl_ = new ModelGL;
  model_gl_->SetData(model_);

  //creat render_device_fbo
  V_RET(device_ = new RenderDeviceFBO);
  V_RET(dual_peeling_device_ = new RenderDeviceFBO);
  V_RET(bucket_peeling_device_ = new RenderDeviceFBO);
  V_RET(this->InitFrontPeelingRenderTargets());
  V_RET(this->InitDualPeelingRenderTargets());
  V_RET(this->InitBucketPeelingRenderTargets());
  V_RET(this->LoadShaders());
  V_RET(quad_display_list_ = MakeFullScreenQuad());

  //glDisable(GL_CULL_FACE);
  //lighting is disabled as a default?
  //glDisable(GL_LIGHTING);
  //I still don't see a case to show how GL_NORMALIZE works
  //glDisable(GL_NORMALIZE);
  demo_ready_ = true;
  alpha_ = 0.6;
  translate_y_ = 0;
  peeling_method_ = kBucket;
  return true;
}

void DepthPeelingDemo::OnKeyboard(unsigned char key, int x, int y) {
  const float translate_step = 0.05;
  switch (key) {
    case 'a':
      camera_.Yaw(-1);
      break;
    case 'd':
      camera_.Yaw(1);
      break;
    case 'w':
      camera_.Walk(1);
      break;
    case 's':
      camera_.Walk(-1);
      break;
    case 'q':
      camera_.Pitch(1);
      break;
    case 'z':
      camera_.Pitch(-1);
      break;
    case 'j':
      translate_y_ += translate_step;
      break;
    case 'k': 
      translate_y_ -= translate_step;
      break;
    case '1':
      peeling_method_ = kNone;
      break;
    case '2':
      peeling_method_ = kDual;
      break;
    case '3':
      peeling_method_ = kFront2Back;
      break;
    case '4':
      peeling_method_ = kBucket;
      break;
    default:
      return;
  }
  ReDraw();
}

void DepthPeelingDemo::OnResize(int width, int height) {
  if (this->width() != width || this->height() != height) {
    demo_ready_ = false;
    this->set_width(width); this->set_height(height);
    this->DeleteFrontPeelingRenderTargets();
    RET(this->InitFrontPeelingRenderTargets());
    this->DeleteDualPeelingRenderTargets();
    RET(this->InitDualPeelingRenderTargets());
    this->DeleteBucketPeelingRenderTargets();
    RET(this->InitBucketPeelingRenderTargets());
    demo_ready_ = true;
  }
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  camera_.set_aspect_ratio(static_cast<float>(width)/height);
  camera_.ApplyProjectionTransform();
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void DepthPeelingDemo::OnIdle() {
  ReDraw();
}

void DepthPeelingDemo::OnDisplay() {
  RET(demo_ready_);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  camera_.ApplyViewTransform();
  glTranslatef(0, translate_y_, 0);

  switch (peeling_method_) {
    case kDual:
      this->RenderDualDepthPeeling();     
      break;
    case kFront2Back:
      this->RenderFrontToBackPeeling();      
      break;
    case kBucket:
      this->RenderBucketDepthPeeling();
      break;
    case kNone:
      this->RenderGL();
      break;
    default:
      return;
  }  
  DisplayStastics(method_name[peeling_method_], fps_counter_.Update(), "fps");
  glutSwapBuffers();  
}

void DepthPeelingDemo::RenderModel() {
  if (!model_gl_->Draw()) {
    demo_ready_ = false;
  }
}

void DepthPeelingDemo::RenderModelBound() {
  model_gl_->DrawBound();
}

void DepthPeelingDemo::RenderGL() {
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat mat_shininess[] = { 50.0 };
  GLfloat mat_diffuse[] = { 0., 0., 1.0, 1.0 };
  Point pos = camera_.position();
  GLfloat light_position[] = { pos.x, pos.y, pos.z, 0.0 };

  glClearColor (0.0, 0.0, 0.0, 0.0);

//  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
 // glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_FLAT);

  this->RenderModel();
  //glutSolidSphere(0.1, 32, 32);
  glDisable(GL_LIGHT0);
  glDisable(GL_LIGHTING);
}

bool DepthPeelingDemo::LoadShaders() {
  front_init_program_ = new ProgramGLSL;
  front_peeling_program_ = new ProgramGLSL;
  front_blend_program_ = new ProgramGLSL;
  front_final_program_ = new ProgramGLSL;
  ShaderObject shade_vertex(GL_VERTEX_SHADER, "shaders/shade_vertex.glsl",
                            FILE_SOURCE);
  ShaderObject shade_fragment(GL_FRAGMENT_SHADER, 
                              "shaders/shade_fragment.glsl", FILE_SOURCE);
  front_init_program_->Load("shaders/front_peeling_init_vertex.glsl",
                            "shaders/front_peeling_init_fragment.glsl");
  front_init_program_->AttachShader(shade_vertex);
  front_init_program_->AttachShader(shade_fragment);
  front_init_program_->Link();
  front_peeling_program_->Load("shaders/front_peeling_peel_vertex.glsl",
                               "shaders/front_peeling_peel_fragment.glsl");
  front_peeling_program_->AttachShader(shade_vertex);
  front_peeling_program_->AttachShader(shade_fragment);
  front_peeling_program_->Link();

  front_blend_program_->Load("shaders/front_peeling_blend_vertex.glsl",
                             "shaders/front_peeling_blend_fragment.glsl");
  front_blend_program_->Link();
  
  front_final_program_->Load("shaders/front_peeling_final_vertex.glsl",
                             "shaders/front_peeling_final_fragment.glsl");
  front_final_program_->Link();

  dual_init_program_ = new ProgramGLSL;
  dual_peel_program_ = new ProgramGLSL;
  dual_blend_program_ = new ProgramGLSL;
  dual_final_program_ = new ProgramGLSL;
  dual_init_program_->Load("shaders/dual_peeling_init_vertex.glsl",
                           "shaders/dual_peeling_init_fragment.glsl");
  dual_init_program_->Link();

  dual_peel_program_->Load("shaders/dual_peeling_peel_vertex.glsl",
                           "shaders/dual_peeling_peel_fragment.glsl");
  dual_peel_program_->AttachShader(shade_vertex);
  dual_peel_program_->AttachShader(shade_fragment);
  dual_peel_program_->Link();

  dual_blend_program_->Load("shaders/dual_peeling_blend_vertex.glsl",
                            "shaders/dual_peeling_blend_fragment.glsl");
  dual_blend_program_->Link();

  dual_final_program_->Load("shaders/dual_peeling_final_vertex.glsl",
                            "shaders/dual_peeling_final_fragment.glsl");
  dual_final_program_->Link();

  bucket_init_program_ = new ProgramGLSL;
  bucket_peel_program_ = new ProgramGLSL;
  bucket_final_program_ = new ProgramGLSL;
  bucket_init_program_->Load("shaders/min_max_depth_vertex.glsl",
                             "shaders/min_max_depth_fragment.glsl");
  bucket_init_program_->Link();

  bucket_peel_program_->Load("shaders/bucket_peeling_vertex.glsl",
                             "shaders/bucket_peeling_fragment.glsl");
  bucket_peel_program_->AttachShader(shade_vertex);
  bucket_peel_program_->AttachShader(shade_fragment);
  bucket_peel_program_->Link();

  bucket_final_program_->Load("shaders/bucket_peeling_final_vertex.glsl",
                              "shaders/bucket_peeling_final_fragment.glsl");
  bucket_final_program_->AttachShader(shade_vertex);
  bucket_final_program_->AttachShader(shade_fragment);
  bucket_final_program_->Link();
  return true;
}

void DepthPeelingDemo::ReleaseShaders() {
  delete front_init_program_;
  delete front_peeling_program_;
  delete front_blend_program_;
  delete front_final_program_;
}

bool DepthPeelingDemo::InitFrontPeelingRenderTargets() {
  for (int i = 0; i < 2; ++i) {
    front_color_targets_[i] = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
    V_RET(front_color_targets_[i]->Initialize(
        this->width(), this->height(), GL_RGBA));
    front_depth_targets_[i] = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
    V_RET(front_depth_targets_[i]->Initialize(
        this->width(), this->height(), GL_DEPTH_COMPONENT32, kDepthTexture));
  }
  front_color_blender_target_ = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
  V_RET(front_color_blender_target_->Initialize(
      this->width(), this->height(),  GL_RGBA));
  if (CheckErrorsGL("InitFrontPeelingRenderTargets")) return false;
  return true;
}
void DepthPeelingDemo::DeleteFrontPeelingRenderTargets() {
  Utils::Delete(front_color_blender_target_);
  Utils::Delete(front_depth_targets_, 2);
  Utils::Delete(front_color_targets_, 2);
}

bool DepthPeelingDemo::InitDualPeelingRenderTargets() {
  if (dual_peeling_device_ == NULL) {
    Error("dual peeling device not initialized");
    return false;
  }
  for (int i = 0; i < 2; ++i) {
    dual_depth_targets_[i] = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
    V_RET(dual_depth_targets_[i]->Initialize(
        this->width(), this->height(), GL_RG32F));
    dual_front_blender_targets_[i] = new ImageTextureGL(
        GL_TEXTURE_RECTANGLE_ARB);
    V_RET(dual_front_blender_targets_[i]->Initialize(
        this->width(), this->height(), GL_RGBA));
    dual_back_temp_targets_[i] = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
    V_RET(dual_back_temp_targets_[i]->Initialize(
        this->width(), this->height(), GL_RGBA));
  }
  dual_back_blender_target_ = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
  V_RET(dual_back_blender_target_->Initialize(
      this->width(), this->height(), GL_RGB));
  const ImageTextureGL *mrt[7] = { dual_depth_targets_[0], 
                             dual_front_blender_targets_[0],
                             dual_back_temp_targets_[0],
                             dual_depth_targets_[1], 
                             dual_front_blender_targets_[1],
                             dual_back_temp_targets_[1],
                             dual_back_blender_target_ };
  dual_peeling_device_->Activate();
  dual_peeling_device_->SetRenderTargets(7, mrt);
  dual_peeling_device_->Check();
  dual_peeling_device_->Deactivate();
  if (CheckErrorsGL("InitDualPeelingRenderTargets")) return false;
  return true;
}

void DepthPeelingDemo::DeleteDualPeelingRenderTargets() {
  Utils::Delete(dual_back_blender_target_);
  Utils::Delete(dual_back_temp_targets_, 2);
  Utils::Delete(dual_front_blender_targets_, 2);
  Utils::Delete(dual_depth_targets_, 2);
}

void DepthPeelingDemo::DeleteBucketPeelingRenderTargets() {
  Utils::Delete(bucket_min_max_depth_target_);
  Utils::Delete(bucket_depth_targets_, 8);
}

bool DepthPeelingDemo::InitBucketPeelingRenderTargets() {
  for (int i = 0; i < 8; ++i) {
    bucket_depth_targets_[i] = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
    V_RET(bucket_depth_targets_[i]->Initialize(
        this->width(), this->height(), GL_RGBA32F));
  }
  bucket_min_max_depth_target_ = new ImageTextureGL(GL_TEXTURE_RECTANGLE_ARB);
  V_RET(bucket_min_max_depth_target_->Initialize(
      this->width(), this->height(), GL_RG32F));
  bucket_peeling_device_->Activate();
  bucket_peeling_device_->SetRenderTargets(8, bucket_depth_targets_);
  bucket_peeling_device_->Check();
  bucket_peeling_device_->Deactivate();
  if (CheckErrorsGL("InitBucketPeelingRenderTargets")) return false;
  return true;
}

bool DepthPeelingDemo::InitAccumulationRenderTargets() {
  return true;
}
void DepthPeelingDemo::DeleteAccumulationRenderTargets() {
}

void DepthPeelingDemo::RenderFrontToBackPeeling() {
  device_->Activate(); 
  device_->SetRenderTarget(front_color_blender_target_);
  device_->SetDepthBuffer(front_depth_targets_[0]);
  device_->Check();
  glClearColor(0, 0, 0, 1);
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  front_init_program_->Begin();
  GLint alpha_var = front_init_program_->GetUniformVar("Alpha");
  glUniform1f(alpha_var, alpha_);
  //SeqTimer::TimerBegin("draw model");
  this->RenderModel();
  //SeqTimer::TimerEnd();

  CheckErrorsGL("RenderFrontToBackPeeling");
  int num_layers = 15;
  for (int layer = 1; layer < num_layers; ++layer) {
    int curr_id = layer % 2;
    int prev_id = 1 - curr_id;
    device_->SetRenderTarget(front_color_targets_[curr_id]);
    device_->SetDepthBuffer(front_depth_targets_[curr_id]);
    //glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
    front_peeling_program_->Begin();
    glActiveTexture(GL_TEXTURE0);
    front_depth_targets_[prev_id]->Bind();
    GLint depth_tex_var = front_peeling_program_->GetUniformVar("DepthTex");
    alpha_var = front_peeling_program_->GetUniformVar("Alpha");
    glUniform1f(alpha_var, alpha_);
    glUniform1i(depth_tex_var, 0);
    this->RenderModel();
    CheckErrorsGL("Peeling");
   
    device_->SetRenderTarget(front_color_blender_target_);
    device_->SetDepthBuffer(front_depth_targets_[0]);
    //glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
    
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
    front_blend_program_->Begin();
    front_color_targets_[curr_id]->Bind();
    GLint temp_tex_var = front_blend_program_->GetUniformVar("TempTex");
    glUniform1i(temp_tex_var, 0);
    glCallList(quad_display_list_);
    glDisable(GL_BLEND);
    CheckErrorsGL("Blend");
  }

  device_->Deactivate();
  glDisable(GL_DEPTH_TEST);
  //glDrawBuffer(GL_FRONT);
  float back_ground_color[3] = { 1, 1, 1 };
  front_final_program_->Begin();
  CheckErrorsGL("Final");
  front_color_blender_target_->Bind();
  GLint back_ground_color_var = front_final_program_->GetUniformVar(
      "BackgroundColor");
  GLint color_tex_var = front_final_program_->GetUniformVar("ColorTex");
  glUniform1i(color_tex_var, 0);
  glUniform3fv(back_ground_color_var, 1, back_ground_color);
  glCallList(quad_display_list_);
  ProgramGLSL::Disable();
}

void DepthPeelingDemo::RenderDualDepthPeeling() {
  glDisable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  dual_peeling_device_->Activate();
  glDrawBuffers(2, &mrt_[1]);
  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT);

  glDrawBuffer(mrt_[0]);
  glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT);
  glBlendEquationEXT(GL_MAX_EXT);

  dual_init_program_->Begin();
  this->RenderModel();
  CheckErrorsGL("init");

  glDrawBuffer(mrt_[6]);
  glClearColor(1, 1, 1, 0);
  glClear(GL_COLOR_BUFFER_BIT);
  int curr_id = 0;
  for (int pass = 1; pass < 9; ++pass) {
    curr_id = pass % 2;
    int prev_id = 1 - curr_id;
    int target_id = curr_id * 3;

    glDrawBuffers(2, &mrt_[target_id+1]);
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    glDrawBuffer(mrt_[target_id]);
    glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glDrawBuffers(3, &mrt_[target_id]);
    glBlendEquationEXT(GL_MAX_EXT);

    dual_peel_program_->Begin();
    GLint depth_blender_tex_var = dual_peel_program_->GetUniformVar(
        "DepthBlenderTex");
    GLint front_blender_tex_var = dual_peel_program_->GetUniformVar(
        "FrontBlenderTex");
    GLint alpha_var = dual_peel_program_->GetUniformVar("Alpha");
    glActiveTexture(GL_TEXTURE0);
    dual_depth_targets_[prev_id]->Bind();
    glActiveTexture(GL_TEXTURE1);
    dual_front_blender_targets_[prev_id]->Bind();
    glUniform1i(depth_blender_tex_var, 0);
    glUniform1i(front_blender_tex_var, 1);
    glUniform1f(alpha_var, alpha_);
    this->RenderModel();
    CheckErrorsGL("peeling");

    glDrawBuffer(mrt_[6]);
    glBlendEquationEXT(GL_FUNC_ADD);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    dual_blend_program_->Begin();
    GLint temp_tex_var = dual_blend_program_->GetUniformVar("TempTex");
    glActiveTexture(GL_TEXTURE0);
    dual_back_temp_targets_[curr_id]->Bind();
    glUniform1i(temp_tex_var, 0);
    glCallList(quad_display_list_);
    CheckErrorsGL("full screen");
  }

  glDisable(GL_BLEND);
  dual_peeling_device_->Deactivate();
  dual_final_program_->Begin();
  GLint depth_blender_tex_var = dual_final_program_->GetUniformVar(
      "DepthBlenderTex");
  GLint front_blender_tex_var = dual_final_program_->GetUniformVar(
      "FrontBlenderTex");
  GLint back_blender_tex_var = dual_final_program_->GetUniformVar(
      "BackBlenderTex");
  glActiveTexture(GL_TEXTURE0);
  dual_depth_targets_[curr_id]->Bind();
  glActiveTexture(GL_TEXTURE1);
  dual_front_blender_targets_[curr_id]->Bind();
  glActiveTexture(GL_TEXTURE2);
  dual_back_blender_target_->Bind();
  //glUniform1i(depth_blender_tex_var, 0);
  glUniform1i(front_blender_tex_var, 1);
  glUniform1i(back_blender_tex_var, 2);
  glCallList(quad_display_list_);
  ProgramGLSL::Disable();
  CheckErrorsGL("final");
}

void DepthPeelingDemo::RenderBucketDepthPeeling() {
  //glDisable(GL_CULL_FACE);
  glDisable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  device_->Activate();
  device_->SetRenderTarget(bucket_min_max_depth_target_);  
  device_->DisableDepthBuffer();
  device_->Check();
  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT);
  glBlendEquationEXT(GL_MAX_EXT);

  bucket_init_program_->Begin();  
  this->RenderModelBound();
  CheckErrorsGL("init");
  //bucket_min_max_depth_target_->Save("min_max.jpg");

  bucket_peeling_device_->Activate();
  glDrawBuffers(8, mrt_);  
  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT); 
  bucket_peel_program_->Begin();
  GLint alpha_var = bucket_peel_program_->GetUniformVar("Alpha");
  GLint min_max_depth_var = bucket_peel_program_->GetUniformVar(
      "MinMaxDepthTex");
  glActiveTexture(GL_TEXTURE0);
  bucket_min_max_depth_target_->Bind();  
  glUniform1i(min_max_depth_var, 0);
  glUniform1f(alpha_var, alpha_);  
  this->RenderModel();
  bucket_peeling_device_->Deactivate();
  CheckErrorsGL("peel");
  /*bucket_depth_targets_[0]->Save("tex0.jpg");
  bucket_depth_targets_[1]->Save("tex1.jpg");
  bucket_depth_targets_[2]->Save("tex2.jpg");
  bucket_depth_targets_[3]->Save("tex3.jpg");
  bucket_depth_targets_[4]->Save("tex4.jpg");
  bucket_depth_targets_[5]->Save("tex5.jpg");
  bucket_depth_targets_[6]->Save("tex6.jpg");
  bucket_depth_targets_[7]->Save("tex7.jpg");*/

  glDisable(GL_BLEND);  
  bucket_final_program_->Begin();
  const char *tex_names[] = { "Tex0", "Tex1", "Tex2", "Tex3", "Tex4", "Tex5", 
    "Tex6", "Tex7" };
  GLint tex_vars[8];
  for (int i = 0; i < 8; ++i) {
    tex_vars[i] = bucket_final_program_->GetUniformVar(tex_names[i]);
    glActiveTexture(GL_TEXTURE0+i);
    bucket_depth_targets_[i]->Bind();
    glUniform1i(tex_vars[i], i);
  }   
  glCallList(quad_display_list_);
  ProgramGLSL::Disable();
  CheckErrorsGL("final"); 
}

} // namespace venus
